package com.lvchangqi.util;

import com.lvchangqi.entity.LeaveEntity;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Component;

import javax.print.Doc;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.time.LocalDate;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by version on 17-6-20.
 */
@Component
public class DocBuilder {

    private Map<String, String> getData(LeaveEntity leaveEntity) throws Exception {
        String[] from = leaveEntity.getFrom().split("-");
        String[] to = leaveEntity.getEnd().split("-");
        String start = from[0] + "年" + from[1] + "月" + from[2] + "日" + course2Time(from[3]) + "时";
        String end = to[0] + "年" + to[1] + "月" + to[2] + "日" + course2Time(to[3]) + "时";
        leaveEntity.setFrom(start);
        leaveEntity.setEnd(end);

        LocalDate time = LocalDate.now();
        String confirm = time.getYear() + " 年 " +
                time.getMonthValue() + " 月 " +
                time.getDayOfMonth() + " 日 ";

        String day = this.parseDay(from[1], from[2], to[1], to[2]);

        Map<String, String> data = this.convert2Map(leaveEntity);
        data.put("types", leaveEntity.getTypes() == 0 ? "事假" : "病假");
        data.put("confirm", confirm);
        data.put("day", day);

        return data;
    }

    private String course2Time(String course) {
        String result = null;
        switch (course) {
            case "12":
                result = "8";
                break;
            case "34":
                result = "10";
                break;
            case "56":
                result = "14";
                break;
            case "78":
                result = "16";
                break;
        }

        return result;
    }

    private String parseDay(String f_month, String f_day, String t_month, String t_day) {
        Integer mDay = (Integer.valueOf(t_month) - Integer.valueOf(f_month)) * 30;
        Integer dDay = Integer.valueOf(t_day) - Integer.valueOf(f_day);

        return String.valueOf(mDay + dDay + 1);
    }

    private <T> Map<String, String> convert2Map(T t) throws InvocationTargetException,
            IllegalAccessException {
        Map<String, String> result = new HashMap<>();
        Class clazz = t.getClass();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith("get") && !methodName.contains("Class")) {
                Object value = method.invoke(t);
                if (value != null)
                    result.put(methodName.substring(3).toLowerCase(), value.toString());
            }
        }

        return result;
    }

    private TreeMap<Integer, XWPFRun> getPosToRuns(XWPFParagraph paragraph) {
        int pos = 0;
        TreeMap<Integer, XWPFRun> map = new TreeMap<Integer, XWPFRun>();
        for (XWPFRun run : paragraph.getRuns()) {
            String runText = run.text();
            if (runText != null && runText.length() > 0) {
                for (int i = 0; i < runText.length(); i++) {
                    map.put(pos + i, run);
                }
                pos += runText.length();
            }

        }
        return map;
    }

    private void replace2(XWPFParagraph p, Map<String, String> data) {
        String pText = p.getText(); // complete paragraph as string
        if (pText.contains("${")) { // if paragraph does not include our pattern, ignore
            TreeMap<Integer, XWPFRun> posRuns = getPosToRuns(p);
            Pattern pat = Pattern.compile("\\$\\{(.+?)\\}");
            Matcher m = pat.matcher(pText);
            while (m.find()) { // for all patterns in the paragraph
                String g = m.group(1);  // extract key start and end pos
                int s = m.start(1);
                int e = m.end(1);
                String key = g;
                String x = data.get(key);
                if (x == null)
                    x = "";
                SortedMap<Integer, XWPFRun> range = posRuns.subMap(s - 2, true, e + 1, true); // get runs which contain the pattern
                boolean found1 = false; // found $
                boolean found2 = false; // found {
                boolean found3 = false; // found }
                XWPFRun prevRun = null; // previous run handled in the loop
                XWPFRun found2Run = null; // run in which { was found
                int found2Pos = -1; // pos of { within above run
                for (XWPFRun r : range.values())
                {
                    if (r == prevRun)
                        continue; // this run has already been handled
                    if (found3)
                        break; // done working on current key pattern
                    prevRun = r;
                    for (int k = 0;; k++) { // iterate over texts of run r
                        if (found3)
                            break;
                        String txt = null;
                        try {
                            txt = r.getText(k); // note: should return null, but throws exception if the text does not exist
                        } catch (Exception ex) {

                        }
                        if (txt == null)
                            break; // no more texts in the run, exit loop
                        if (txt.contains("$") && !found1) {  // found $, replace it with value from data map
                            txt = txt.replaceFirst("\\$", x);
                            found1 = true;
                        }
                        if (txt.contains("{") && !found2 && found1) {
                            found2Run = r; // found { replace it with empty string and remember location
                            found2Pos = txt.indexOf('{');
                            txt = txt.replaceFirst("\\{", "");
                            found2 = true;
                        }
                        if (found1 && found2 && !found3) { // find } and set all chars between { and } to blank
                            if (txt.contains("}"))
                            {
                                if (r == found2Run)
                                { // complete pattern was within a single run
                                    txt = txt.substring(0, found2Pos)+txt.substring(txt.indexOf('}'));
                                }
                                else // pattern spread across multiple runs
                                    txt = txt.substring(txt.indexOf('}'));
                            }
                            else if (r == found2Run) // same run as { but no }, remove all text starting at {
                                txt = txt.substring(0,  found2Pos);
                            else
                                txt = ""; // run between { and }, set text to blank
                        }
                        if (txt.contains("}") && !found3) {
                            txt = txt.replaceFirst("\\}", "");
                            found3 = true;
                        }
                        r.setText(txt, k);
                    }
                }
            }
        }

    }

    private void addPicture(XWPFDocument doc, List<InputStream> pic) throws Exception{
        for(XWPFTable tbl: doc.getTables()){
            XWPFTableRow row = tbl.getRows().get(4);
            XWPFTableCell cell = row.getTableCells().get(1);
            List<XWPFParagraph> paragraphs = cell.getParagraphs();
            int format = XWPFDocument.PICTURE_TYPE_PNG;
            for(int i = 0; i<paragraphs.size();i++){
                if(i == 1 || i == 3){
                    XWPFParagraph p = paragraphs.get(i);
                    p.setAlignment(ParagraphAlignment.CENTER);
                    XWPFRun run = p.getRuns().get(0);
                    run.addPicture(pic.get(0), format, "签", Units.pixelToEMU(51), Units.pixelToEMU(29));
                    pic.remove(0);
                }
            }
        }

    }

    public InputStream build(LeaveEntity leaveEntity, List<InputStream> in) throws Exception {
        Map<String, String> data = this.getData(leaveEntity);

        InputStream inDoc = new URL("http://bucket-vacate.nos-eastchina1.126.net/template.docx").openStream();
        XWPFDocument doc = new XWPFDocument(inDoc);

        for (XWPFParagraph p : doc.getParagraphs()) {
            replace2(p, data);
        }
        for (XWPFTable tbl : doc.getTables()) {
            for (XWPFTableRow row : tbl.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph p : cell.getParagraphs()) {
                        replace2(p, data);
                    }
                }
            }
        }
        this.addPicture(doc, in);

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        doc.write(out);

        doc.close();
        inDoc.close();
        for (InputStream ins:in) {
            ins.close();
        }

        return new ByteArrayInputStream(out.toByteArray());
    }
}
